package com.huluobo.lib_base

import android.util.Log
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.blankj.utilcode.util.ToastUtils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.launch
import java.io.File
import java.lang.Exception

/**
 *  Created by LC on 2025/7/22.
 *  所有ViewModel的基准类,主要功能是提供请求网络,请求数据库,上传下载,扫描文件等获取数据的方法
 *  数据流转的过程
 *  1.当具体的ViewModel(登录的,或者注册的)初始化的时候,一直在管道中不停的接收到来的意图
 *  2.当用户再View中产生了数据获取的想法的时候,需要发送一个意图来触发
 *  3.意图被存放在管道中,通过具体的ViewModel来处理意图
 */
abstract class BaseViewModel<uiIntent : UIIntent> : ViewModel() {

    //存放意图的管道(频道) UNLIMITED 代表当前的频道数量没有最小值
    private val _uiIntent = Channel<UIIntent> { Channel.UNLIMITED }

    //可变的状态,并且将状态抓换为一个流,泛型是所有状态的密封类
    //默认会产生一个读取的状态
    //由于是被private修饰的,所以需要一个对外暴露的get方法
    private var _state = MutableStateFlow<UIState>(UIState.OnLoading)
    val state: StateFlow<UIState>
        get() = _state

    //初始化
    init {
        viewModelScope.launch {
            //将所有的意图接收,并且存放到管道(频道)当中
            _uiIntent.receiveAsFlow().collect {
                handleIntent(it)
            }
        }
    }

    //所有继承当前ViewModel的类,都需要实现该方法,来处理对应的意图
    //比如登录的Intent,需要处理登录的意图,到底是请求网络还是请求数据库
    abstract fun handleIntent(it: UIIntent)

    //发送意图的方法
    //当页面中需要有请求数据的时候,需要调用该方法并传入一个意图
    fun sendIntent(intent: UIIntent) {
        viewModelScope.launch {
            _uiIntent.send(intent)
        }
    }

    //请求的方法,所有请求方法的返回值都是一个Flow
    fun httpRequest(request: Flow<Res<Any>>, type: StateType = StateType.DEFAULT) {
        viewModelScope.launch {
            //Dispatchers调度器
            //将请求方法转换到子线程中运行
            request.flowOn(Dispatchers.IO)
                //捕获异常
                .catch {
                    ToastUtils.showLong("网络请求错误:${it.message}")
                    Log.e("TagA", "网络请求错误:${it.message}")
                    //接受请求返回的值,成功的赋值和失败的赋值
                }.collect {
                    if (it.code == 0 || it.code == 200) {
                        _state.value = UIState.OnSuccess(it.data, type)
                    } else {
                        _state.value = UIState.OnFailed(it.msg)
                    }
                }
        }
    }

    //下载的方法
    fun download(url: String, file: File) {
        viewModelScope.launch {
            DownloadUtils.download(url, file)
                .flowOn(Dispatchers.IO)
                .catch {
                    Log.e("TagA", "下载错误:${it.message}")
                }.collect {
                    _state.value = it
                    Log.e("TagA", "下载接收:${_state.value}")
                }
        }
    }

    //扫描本地文件,当前file代表是一个文件夹,在kotlin或者Android中,file不光嗲表一个文件,也可以是一个文件夹
    fun scanFile(file: File) {
        viewModelScope.launch {
            try {
                //遍历文件夹中的文件,形成一个集合
                val files = file.listFiles()

                //创建一个集合,其中的类型是自己封装的本地文件类型
                val list = mutableListOf<LocalFileEntity>()

                for (i in files) {
                    //在集合中添加文件名,和文件的绝对路径
                    list.add(LocalFileEntity(i.name, i.absolutePath))
                }
                //循环添加完毕,给成功的状态赋值
                _state.value = UIState.OnSuccess(list)

            } catch (e: Exception) {
                //扫描失败
                _state.value = UIState.OnFailed(e.message.toString())
            }
        }
    }

    fun roomRequest(request: Flow<Any>, type: StateType = StateType.DEFAULT) {
        viewModelScope.launch {
            request.flowOn(Dispatchers.IO)
                .catch {
                    Log.e("TagA", "数据库错误:${it.message}")
                }.collect {
                    if (it != null) {
                        _state.value = UIState.OnSuccess(it, type)
                    } else {
                        _state.value = UIState.OnFailed("数据库查询错误,查询为空")
                    }
                }
        }
    }
}

//封装本地文件实体类,包含文件名和文件路径
data class LocalFileEntity(val fileName: String, val filePath: String)